/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.amqp.rabbitmq.factory;

import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.util.Assert;
import org.tianyun.cloud.amqp.rabbitmq.configuration.RabbitMqProperties;
import org.tianyun.cloud.config.console.ConsoleBean;

import java.util.Properties;

/**
 * 受托管的CachingConnectionFactory
 *
 * @auther ebert_chan
 */
public class ManagedRabbitConnectionFactory implements ConnectionFactory, ConsoleBean, DisposableBean, BeanNameAware, ApplicationEventPublisherAware, ApplicationListener<ContextClosedEvent>,
        ShutdownListener, ApplicationContextAware {

    private static final Logger LOGGER = LoggerFactory.getLogger(ManagedRabbitConnectionFactory.class);

    private CachingConnectionFactory delegate;

    private Properties startupArguments;

    public ManagedRabbitConnectionFactory(RabbitMqProperties properties) {
        delegate = new CachingConnectionFactory();
        configure(properties);
    }

    public void configure(RabbitMqProperties properties) {
        LOGGER.info("----------------------------------------------------------------------------------------------");
        LOGGER.info("RabbitMQ address: {}", properties.getAddress());
        LOGGER.info("RabbitMQ username: {}", properties.getUsername());
        LOGGER.info("RabbitMQ password: {}", "******");
        LOGGER.info("RabbitMQ virtual-host: {}", properties.getVirtualHost());
        LOGGER.info("RabbitMQ acknowledge-mode: {}", properties.getAcknowledgeMode());
        LOGGER.info("----------------------------------------------------------------------------------------------");

        Assert.hasText(properties.getAddress(),
                "Failed to create the rabbit connection factory with @RabbitContainer annotation, this argument 'address' is required; it must not be empty");
        Assert.hasText(properties.getUsername(),
                "Failed to create the rabbit connection factory with @RabbitContainer annotation, this argument 'username' is required; it must not be empty");
        Assert.hasText(properties.getPassword(),
                "Failed to create the rabbit connection factory with @RabbitContainer annotation, this argument 'password' is required; it must not be empty");
        Assert.hasText(properties.getVirtualHost(),
                "Failed to create the rabbit connection factory with @RabbitContainer annotation, this argument 'virtualHost' is required; it must not be empty");

        delegate.setAddresses(properties.getAddress());
        delegate.setUsername(properties.getUsername());
        delegate.setPassword(properties.getPassword());
        delegate.setVirtualHost(properties.getVirtualHost());
        delegate.setPublisherConfirmType(properties.getPublisherConfirmType());
        delegate.setPublisherReturns(true);

        startupArguments = new Properties();
        startupArguments.put("rabbitmq.address", properties.getAddress());
        startupArguments.put("rabbitmq.virtual-host", properties.getVirtualHost());
        startupArguments.put("rabbitmq.acknowledge-mode", properties.getAcknowledgeMode());
    }

    @Override
    public Properties startupArguments() {
        return startupArguments;
    }

    @Override
    public ConnectionFactory getPublisherConnectionFactory() {
        return delegate.getPublisherConnectionFactory();
    }

    @Override
    public boolean isSimplePublisherConfirms() {
        return delegate.isSimplePublisherConfirms();
    }

    @Override
    public boolean isPublisherConfirms() {
        return delegate.isPublisherConfirms();
    }

    @Override
    public boolean isPublisherReturns() {
        return delegate.isPublisherReturns();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.delegate.setApplicationContext(applicationContext);
    }

    @Override
    public Connection createConnection() throws AmqpException {
        return delegate.createConnection();
    }

    @Override
    public String getHost() {
        return delegate.getHost();
    }

    @Override
    public int getPort() {
        return delegate.getPort();
    }

    @Override
    public String getVirtualHost() {
        return delegate.getVirtualHost();
    }

    @Override
    public String getUsername() {
        return delegate.getUsername();
    }

    @Override
    public void addConnectionListener(ConnectionListener listener) {
        delegate.addConnectionListener(listener);
    }

    @Override
    public boolean removeConnectionListener(ConnectionListener listener) {
        return delegate.removeConnectionListener(listener);
    }

    @Override
    public void clearConnectionListeners() {
        delegate.clearConnectionListeners();
    }

    @Override
    public void shutdownCompleted(ShutdownSignalException cause) {
        delegate.shutdownCompleted(cause);
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        delegate.onApplicationEvent(event);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        delegate.setApplicationEventPublisher(applicationEventPublisher);
    }

    @Override
    public void setBeanName(String name) {
        delegate.setBeanName(name);
    }

    @Override
    public void destroy() throws Exception {
        delegate.destroy();
    }

}
